let McScore = {
	createNew:function (option) {
		//public
		let ins = {
			sid:-1,
			jsonLoaded :false,
			scores:null,
			pages:0,
			svgRoot:[],
			svgPageLines:[],
			isCallOnReady:false,
			curPage:-1,
			curLine:-1,
			curMeasure:-1,
			curTick:-1,
			staffOff:[],
			iframeRoot:[],
			noteMap:{},
			status : "INIT",
			staffNoteList:[],
			measureMap : {},
			note2MeasureMap:{},
			scoreBPM:-1,
			currentBPM:-1,
			scoreKeeper:null
	}

		ins.option = {
			enableJsBridge : true,
			onNoteClick : function(element, note){},
			onNoteOn : function(element, note){},
			onNoteOff: function(element, note){},
			onNoteError:function(element,note){},
			onNoteCorrect:function(element,note){},
			onError: function(msg){alert(msg)},
			onReady : function(){},
			onFinish : function(){},
			showScore : true,
		};


		//private
		let prv = {
			currentFrameTick:-1,
			vernier : {},
			scrollOff:-1,
			marginTop:100,
			loop : {
				A:null,
				Aborder:null,
				B:null,
				Bborder:null
			}
	}

		ins.svgRoot.push("")
		ins.svgPageLines.push("")
		ins.iframeRoot.push("")

		prv.makeVernier = function(page){
			var vx = ins.svgPageLines[page][0].x;
			var vy = ins.svgPageLines[page][0].y - 10;
			var vh = ins.svgPageLines[page][0].h + 20;
			prv.vernier = {"page":page};
			prv.vernier.element = ins.makeSVG('rect', {x: vx, y: vy, width:5, height:vh, stroke: 'black', 'stroke-width': 0, fill: 'blue','fill-opacity':0.2,'stroke-opacity':0.9});
			ins.svgRoot[page].appendChild(prv.vernier.element);
		};
		prv.makeLoopBorder = function(note, isA){
			var page = note.page;
			var x = note.x;
			if(isA){
				x = x-2;
			}else{
				x = x+5;
			}
			var y = ins.svgPageLines[page][note.pageLine].y-20;
			var h = ins.svgPageLines[page][note.pageLine].h+40;
			var border = ins.makeSVG('rect', {x: x, y: y, width:3, height:h, stroke: 'black', 'stroke-width': 0, fill: 'green','fill-opacity':0.5,'stroke-opacity':0.9});

			var page = note.page;
			ins.svgRoot[page].appendChild(border);
			return border;
		}
		prv.onTopRectClick = function(rect, e){
			if(ins.status == 'PLAYING'){
				console.log('ins.status = playing   不能点击')
				return;
			}
			var page = $(rect).attr("page");
			var row = $(rect).attr("line");

			var pageLeft = ins.svgRoot[page].getBoundingClientRect().x;
			//alert(pageLeft);
			var realX = e.pageX;
			var realWidth = rect.getBoundingClientRect().width;
			var svgWidth = $(rect).attr("w");
			var svgX = realX*svgWidth/realWidth; //减svg的边距
			var info =  page +":"+ row;
			//var box = JSON.stringify(rect.getBoundingClientRect());
			var sPosPage = "(" + e.pageX + "," + e.pageY + ")";
			var sPosScreen = "(" + svgX + ")";

			var note = ins.findNearestNoteByPos(page, row, svgX);
			ins.pointToNote(note);
			ins.onNoteClick(note);
			// alert(JSON.stringify(e) + JSON.stringify(this) );
		};
		prv.analyzeLines = function(page) {
			var sLines = ins.svgRoot[page].getElementsByClassName("StaffLines"); //所有的线
			//alert(JSON.stringify(sLines));
			var sn = ins.scores.staves.length; //谱表数

			//计算每一行的rect
			var linesInfo = [];
			for(var i=0;i<sLines.length/sn/5;i++){
				var pbs = sLines[sn*5*i].getAttribute("points").split(/[, ]/);
				var pes = sLines[sn*5*(i+1)-1].getAttribute("points").split(/[, ]/);

				var ln = new Object();
				ln.bs = sLines[sn*5*i]; //当前行的第一条线（五线谱上叫第5线）
				ln.x = parseFloat(pbs[0]);
				ln.y = parseFloat(pbs[3]);
				ln.w = parseFloat(pbs[2])-parseFloat(pbs[0]);
				ln.h = parseFloat(pes[1])-parseFloat(pbs[1]);
				ln.bottom = parseFloat(pes[1]);

				// console.log("line: "+ linesInfo.length + " top: "+ln.y+"-" + (ln.y-35).toFixed(0) +" bottom: "+ln.bottom+"-" + (ln.bottom+35).toFixed(0) + " h: " + ln.h);

				linesInfo.push(ln);

				//画辅助线
				var w = ln.w+20;
				var lineHelper = ins.makeSVG('rect', {x: ln.x-10, y: ln.y-30, width:w, height:ln.h+60, stroke: 'green', 'stroke-width': 0, fill: 'blue','fill-opacity':0,'stroke-opacity':1});

				$(lineHelper).addClass('toprect');
				$(lineHelper).attr("page", page);
				$(lineHelper).attr("line", i);
				$(lineHelper).attr("w", w);
				$(lineHelper).click(function(e){prv.onTopRectClick(this, e);});
				ins.svgRoot[page].appendChild(lineHelper);
				//
			}
			ins.svgPageLines[page] = linesInfo;
			//console.log(JSON.stringify(ins.svgPageLines[page]));

		};

		/**
		 根据note信息和svg上的element信息确定当前音符在哪一行，然后移动游标
		 @param e svg上的element
		 @param note json中的note
		 */
		prv.findNoteLine = function (note, page){
			var lines = ins.svgPageLines[page];
			var y = note.y;
			var h = note.h;
			for(var i = 0; i< lines.length;i++){
				if(y >= lines[i].y && y <= lines[i].bottom){  //最简单的情况，刚好落在当前行的内部
					return i;
				}else if(i==0 && y < lines[i].y){ //落在第0行的上部
					return 0;
				}else if(i==lines.length-1 && y > lines[i].bottom){ //落在最后一行的下部
					return i;
				}else if(y > lines[i].bottom){ //落在当前行的下部，暂不能确定位置
					continue;
				}else if(y < lines[i].y && y>lines[i-1].bottom){ //落在当前行的上部，上一行的下部，计算距离哪一个更近
					if(lines[i].y - y > y - lines[i-1].bottom){
						return i - 1;
					}else{
						return i;
					}
				}
			}
			console.log("无法定位对应的行");
			return -1;
		};
		/*********************public methods*********************************/

		ins.getTimeSig = function(){
			return ins.scores.staves[0].measures[0].timesig;
		}

		ins.getBPM = function(){
			return ins.currentBPM;
		}

		ins.setOption = function (option){
			ins.option = option;
		};

		ins.setScoreKeep = function(scoreKeep){
			ins.scoreKeeper = scoreKeep
		}

		ins.makeSVG = function (tag, attrs) {
			var el= document.createElementNS('http://www.w3.org/2000/svg', tag);
			for (var k in attrs)
				el.setAttribute(k, attrs[k]);
			return el;
		};



		ins.resize = function (page){
			var width = $(window).width();
			var oldWidth = $(ins.svgRoot[page]).width();
			var oldHeight = $(ins.svgRoot[page]).height();
			$(ins.svgRoot[page]).height(width/oldWidth*oldHeight);
			$(ins.svgRoot[page]).width(width);
		};

		/**
		 载入指定页面及其后续全部页面的SVG，为了使前面的页面更快的显示出来，不是一次性加载，而是通过定时器每隔一秒加载一个
		 */
		ins.loadPageSvg = function (page){
			// if(ins.option.showScore == false){
			// 	ins.isReady();
			// 	return;
			// }
			if(page > ins.pages){
				return;
			}
			var width = $(window).width();
			var iframe = document.createElement("iframe");
			iframe.id = 'page-'+page;
			// iframe.src = "./scores/"+ins.sid+'/pages/'+page+'.svg';//fixme by cbj
			iframe.src = 'https://gitee.com/HelloRobot/volin/raw/master/1.svg';
			// console.log('web123  svg src='+iframe.src)
			iframe.setAttribute('frameborder', '0');
			iframe.scrolling="no";
			iframe.width = 0;
			iframe.height = 0;

			iframe.onload = function() {   //此事件不兼容IE
				var rt = iframe.getSVGDocument().rootElement;
				var sw = parseInt($(rt).attr("width"));
				var sh = parseInt($(rt).attr("height"));
				iframe.width = width;
				iframe.height = width/sw*sh;
				ins.svgRoot.push(rt);
				ins.resize(page);

				prv.analyzeLines(page);

				$('.Note', this.contentDocument).each(function(){
					//$(this).click(function(){
					//	ins.onNoteClick(this);
					//});

					//为note绑定element
					var id = this.attributes["id"].value;
					for(var i=0;i<100;i++){
						var note = ins.noteMap[id+"-"+i];
						if(!note){
							break;
						}
						var line = prv.findNoteLine(note, page);

						note.page = page;
						note.pageLine = line;
						note.element = this;
						//console.log("Note: " + JSON.stringify(note) );
					}
				});


				ins.isReady();
				setTimeout(function(){ins.loadPageSvg(page+1)}, 1000);

			};

			document.getElementById('score').appendChild(iframe);
			ins.iframeRoot.push(iframe);
		};

		ins.onNoteClick = function(note){
			if(ins.option.onNoteClick && typeof(ins.option.onNoteClick)=="function"){
				//var id=$(e).attr("id");
				ins.option.onNoteClick(note);
				//ins.option.onNoteClick(e, ins.noteMap[id+"-0"]);
			}
		}

		ins.pointToNote = function(note){
			// console.log('point to note '+JSON.stringify(note))
			if(ins.option.showScore == false){
				 console.log(' showScore == false  return')
				return;
			}
			//console.log("point to page "+ note.page+ " note: "+note.id);

			//移动到指定的页
			if(prv.vernier.page != note.page){
				ins.pointToPage(note.page);
			}

			//移动x坐标
			//console.log(prv.vernier.element.getAttribute('x')+":"+prv.vernier.element.getAttribute('y'));
			if(Math.abs(prv.vernier.element.getAttribute('x') - note.x) > 0.1){
				//console.log("move to x : " + note.x);
				prv.vernier.element.setAttribute('x', note.x+1);
			}

			//移动y坐标
			var page = note.page;
			var lines = ins.svgPageLines[note.page];
			var pageLine = note.pageLine;
			//console.log("move to line " + pageLine +", y: " + lines[pageLine].y);

			if(Math.abs(prv.vernier.element.getAttribute('y') - (lines[pageLine].y-10)) > 0.1){
				//console.log("move to y : " + (lines[pageLine].y-10));
				prv.vernier.element.setAttribute('y', lines[pageLine].y-10);
			}
			//prv.vernier.element.setAttribute('y',lines[pageLine].y-10);

			//页内偏移
			var pageOff = prv.vernier.element.getBoundingClientRect().top;

			//计算总偏移，滚动页面
			var off = $(ins.iframeRoot[page]).offset().top+pageOff;
			ins.scrollTo(off);
		}
		ins.scrollTo = function (off){
			if(prv.scrollOff == off){
				return;
			}

			prv.scrollOff = off;
			off = off - prv.marginTop;
			$("html, body").animate({
				scrollTop: off + "px"
			}, {
				duration: 50,
				easing: "swing"
			});

		};


		ins.findNearestNoteByPos = function(page, row, x){
			// console.log("click page: "+page+" row: "+row+" x:" + x);
			var nearest = null;
			var d = 10000;
			for(var i=0;i<ins.staffNoteList.length;i++){
				for(var j=0;j<ins.staffNoteList[i].length;j++){
					var note = ins.staffNoteList[i][j];
					if(note == null){
						continue;
					}
					if(note.page != page){
						continue;
					}
					if(note.pageLine != row){
						continue;
					}
					//console.log(Math.abs(nearest.x-x));
					if(nearest == null){
						nearest = note;
						d = Math.abs(nearest.x-x);
						continue;
					}
					if(Math.abs(note.x-x) < d){
						d = Math.abs(note.x-x);
						nearest = note;
					}
				}
			}
			return nearest;
		}

		ins.pointToPage = function(page){
			//console.log("point to page: " + page);
			if(page && page != prv.vernier.page){
				if(prv.vernier.element){
					$(prv.vernier.element).remove();
				}
				// console.log("move to page: " + page);
				prv.makeVernier(page);
			}
		}

		ins.initStatus = function(){
			//$(prv.vernier.element).remove();
			//prv.vernier = {};
			ins.curPage = 1;   		//当前行
			ins.curLine = -1;   		//当前行
			ins.curMeasure = -1;		//当前小节
			ins.curTick = 0;
			ins.staffOff = []; 		   //staffOff[n] 代表第n行的当前note在数组staffNoteList[n][]中的偏移量
			for(var i=0;i<ins.scores.staves.length;i++){
				ins.staffOff.push(0); //给每个staff的off置0
			}
			prv.currentFrameTick = 0;
			if(ins.option.showScore != false){
				//指向第一页
				ins.pointToPage(1);

			}
		}

		ins.nextMidiFrame = function(notesFromJava){
		    // console.log('notesFromJava='+JSON.stringify(notesFromJava))
            ins.staffNoteList.flatMap(it=>{return it}).filter(it=>{//fixme by cbj
                return notesFromJava.findIndex(id=>{ return  id == it.id})>-1
            }).forEach(n=>{
            	mScoreKeep.noteOn(n)
                ins.pointToNote(n)
                ins.option.onNoteOn(n)
            })

        }

        ins.lastMidiOff = function(notesFromJava){
            ins.staffNoteList.flatMap(it=>{return it}).filter(it=>{//fixme by cbj
                return notesFromJava.findIndex(id=>{ return  id == it.id})>-1
            }).forEach(n=>{
                // ins.pointToNote(n)
                ins.option.onNoteOff(n)
            })
        }

        ins.pkMidiOff = function(notesFromJava){
			ins.staffNoteList.flatMap(it=>{return it}).filter(it=>{//fixme by cbj
				return notesFromJava.findIndex(id=>{ return  id == it.id})>-1
			}).forEach(n=>{
				// ins.pointToNote(n)
				mScoreKeep.noteOff(n)
			})
		}

        ins.userNoteOn = function(pitch){
			mScoreKeep.keyDown(pitch)
		}

        ins.userNoteOff = function(pitch){
			mScoreKeep.keyUp(pitch)
		}

        ins.errorClick = function(notesFromJava){
			ins.staffNoteList.flatMap(it=>{return it}).filter(it=>{//fixme by cbj
				return notesFromJava.findIndex(id=>{ return  id == it.id})>-1
			}).forEach(n=>{
				ins.option.onNoteError(n)
			})
		}

		ins.correctClick = function(notesFromJava){
			ins.staffNoteList.flatMap(it=>{return it}).filter(it=>{//fixme by cbj
				return notesFromJava.findIndex(id=>{ return  id == it.id})>-1
			}).forEach(n=>{
				ins.option.onNoteCorrect(n)
			})
		}

		ins.isReady = function (){
			if(ins.jsonLoaded){
				if(ins.option.showScore != false && ins.svgRoot.length<2){
					console.log("svg尚未就绪");
					return;
				}
				// if(ins.option.enableJsBridge && prv.isJsBridgeReady == false){
				// 	return;
				// }
				//
				// if(ins.player == null || ins.player.isReady() == false){
				// 	console.log("播放器尚未就绪");
				// 	//return;//FIXME by cbj
				// }
				//
				if(!ins.isCallOnReady){
					ins.isCallOnReady = true;
					ins.initStatus();
					console.log("一切就绪");
					ins.option.onReady();
				}
			}
		}
		//获取最后一个音符
		ins.getLastNote = function(){
			var lastNote = null;
			for(var i=0;i<ins.staffNoteList.length;i++){
				if(ins.staffNoteList[i].length == 0){
					continue;
				}
				var note = ins.staffNoteList[i][ins.staffNoteList[i].length-1];
				if(lastNote == null || lastNote.offtick < note.offtick){
					lastNote = note;
				}
			}
			return lastNote;
		}

		ins.setA = function(note){
			if (note==null)return
			prv.loop.A = note
			if (prv.loop.Aborder!=null){
				$(prv.loop.Aborder).remove()
			}
			prv.loop.Aborder = prv.makeLoopBorder(note, true);
		}

		ins.setB = function(note){
			if (note==null)return
			prv.loop.B = note
			if (prv.loop.Bborder!=null){
				$(prv.loop.Bborder).remove()
			}
			prv.loop.Bborder = prv.makeLoopBorder(note, false);
		}

		ins.cancelLoop = function(){
			$(prv.loop.Aborder).remove()
			$(prv.loop.Bborder).remove()
			prv.loop.Aborder = null
			prv.loop.Bborder = null
			prv.loop.A = null
			prv.loop.B = null
		}

		ins.genDataStructure = function(){

			for(var i=0;i<ins.scores.staves.length;i++){
				var n = 0;
				ins.staffNoteList[i] = [];
				for(var j=0; j< ins.scores.staves[i].measures.length; j++){
					var mr = ins.scores.staves[i].measures[j];
					mr.idx = j+1; //从1开始计数的小节序号
					var mid = ins.scores.staves[i].measures[j].id;
					ins.measureMap[mid] = mr;

					for(var k=0; k<ins.scores.staves[i].measures[j].notes.length; k++){
						var note = ins.scores.staves[i].measures[j].notes[k];
						ins.staffNoteList[i][n++] = note;

						var idx = note.id+"-"+note.times;
						note.staff = i;
						note.idx = idx;
						ins.noteMap[idx] = note;

						ins.note2MeasureMap[idx] = mr;
					}
				}
			}

			var lastNote = ins.getLastNote();

			ins.scoreBPM = Math.round(lastNote.offtick/lastNote.offtime/8);
			ins.currentBPM = ins.scoreBPM;
			console.log("socre BPM : " + ins.scoreBPM);
		};

		ins.load = function(){
			//var jsonUrl = '../../midi/'+ins.sid+'/score.json'
            var jsonUrl = 'https://gitee.com/HelloRobot/volin/raw/master/score.json'
			// console.log('web123  load   jsonUrl='+jsonUrl)
			$.ajax({
				type:"get",
				dataType:"text",
				url: jsonUrl,
				success:function(data){
                    console.log('success')
					ins.scores = jQuery.parseJSON(data);
					ins.genDataStructure();
					//
					ins.jsonLoaded = true;
					// ins.isReady();
					ins.pages = ins.scores.p;
					// console.log("ins pages="+ins.pages)
					ins.loadPageSvg(1); //加载第一页svg，后面的会用定时器自动加载
				}
			});
		}

		ins.init = function (id) {
			ins.sid = id
			ins.load()
		}

		ins.setOption(option)

		return ins
	}
}
